/*
 * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.util;

/**
 * Private implementation class for EnumSet, for "regular sized" enum types
 * (i.e., those with 64 or fewer enum constants).
 *
 * @author Josh Bloch
 * @serial exclude
 * @since 1.5
 */
class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {

  private static final long serialVersionUID = 3411599620347842686L;
  /**
   * Bit vector representation of this set.  The 2^k bit indicates the
   * presence of universe[k] in this set.
   */
  private long elements = 0L;

  RegularEnumSet(Class<E> elementType, Enum<?>[] universe) {
    super(elementType, universe);
  }

  void addRange(E from, E to) {
    elements = (-1L >>> (from.ordinal() - to.ordinal() - 1)) << from.ordinal();
  }

  void addAll() {
    if (universe.length != 0) {
      elements = -1L >>> -universe.length;
    }
  }

  void complement() {
    if (universe.length != 0) {
      elements = ~elements;
      elements &= -1L >>> -universe.length;  // Mask unused bits
    }
  }

  /**
   * Returns an iterator over the elements contained in this set.  The
   * iterator traverses the elements in their <i>natural order</i> (which is
   * the order in which the enum constants are declared). The returned
   * Iterator is a "snapshot" iterator that will never throw {@link
   * ConcurrentModificationException}; the elements are traversed as they
   * existed when this call was invoked.
   *
   * @return an iterator over the elements contained in this set
   */
  public Iterator<E> iterator() {
    return new EnumSetIterator<>();
  }

  private class EnumSetIterator<E extends Enum<E>> implements Iterator<E> {

    /**
     * A bit vector representing the elements in the set not yet
     * returned by this iterator.
     */
    long unseen;

    /**
     * The bit representing the last element returned by this iterator
     * but not removed, or zero if no such element exists.
     */
    long lastReturned = 0;

    EnumSetIterator() {
      unseen = elements;
    }

    public boolean hasNext() {
      return unseen != 0;
    }

    @SuppressWarnings("unchecked")
    public E next() {
      if (unseen == 0) {
        throw new NoSuchElementException();
      }
      lastReturned = unseen & -unseen;
      unseen -= lastReturned;
      return (E) universe[Long.numberOfTrailingZeros(lastReturned)];
    }

    public void remove() {
      if (lastReturned == 0) {
        throw new IllegalStateException();
      }
      elements &= ~lastReturned;
      lastReturned = 0;
    }
  }

  /**
   * Returns the number of elements in this set.
   *
   * @return the number of elements in this set
   */
  public int size() {
    return Long.bitCount(elements);
  }

  /**
   * Returns <tt>true</tt> if this set contains no elements.
   *
   * @return <tt>true</tt> if this set contains no elements
   */
  public boolean isEmpty() {
    return elements == 0;
  }

  /**
   * Returns <tt>true</tt> if this set contains the specified element.
   *
   * @param e element to be checked for containment in this collection
   * @return <tt>true</tt> if this set contains the specified element
   */
  public boolean contains(Object e) {
    if (e == null) {
      return false;
    }
    Class<?> eClass = e.getClass();
    if (eClass != elementType && eClass.getSuperclass() != elementType) {
      return false;
    }

    return (elements & (1L << ((Enum<?>) e).ordinal())) != 0;
  }

  // Modification Operations

  /**
   * Adds the specified element to this set if it is not already present.
   *
   * @param e element to be added to this set
   * @return <tt>true</tt> if the set changed as a result of the call
   * @throws NullPointerException if <tt>e</tt> is null
   */
  public boolean add(E e) {
    typeCheck(e);

    long oldElements = elements;
    elements |= (1L << ((Enum<?>) e).ordinal());
    return elements != oldElements;
  }

  /**
   * Removes the specified element from this set if it is present.
   *
   * @param e element to be removed from this set, if present
   * @return <tt>true</tt> if the set contained the specified element
   */
  public boolean remove(Object e) {
    if (e == null) {
      return false;
    }
    Class<?> eClass = e.getClass();
    if (eClass != elementType && eClass.getSuperclass() != elementType) {
      return false;
    }

    long oldElements = elements;
    elements &= ~(1L << ((Enum<?>) e).ordinal());
    return elements != oldElements;
  }

  // Bulk Operations

  /**
   * Returns <tt>true</tt> if this set contains all of the elements
   * in the specified collection.
   *
   * @param c collection to be checked for containment in this set
   * @return <tt>true</tt> if this set contains all of the elements in the specified collection
   * @throws NullPointerException if the specified collection is null
   */
  public boolean containsAll(Collection<?> c) {
    if (!(c instanceof RegularEnumSet)) {
      return super.containsAll(c);
    }

    RegularEnumSet<?> es = (RegularEnumSet<?>) c;
    if (es.elementType != elementType) {
      return es.isEmpty();
    }

    return (es.elements & ~elements) == 0;
  }

  /**
   * Adds all of the elements in the specified collection to this set.
   *
   * @param c collection whose elements are to be added to this set
   * @return <tt>true</tt> if this set changed as a result of the call
   * @throws NullPointerException if the specified collection or any of its elements are null
   */
  public boolean addAll(Collection<? extends E> c) {
    if (!(c instanceof RegularEnumSet)) {
      return super.addAll(c);
    }

    RegularEnumSet<?> es = (RegularEnumSet<?>) c;
    if (es.elementType != elementType) {
      if (es.isEmpty()) {
        return false;
      } else {
        throw new ClassCastException(
            es.elementType + " != " + elementType);
      }
    }

    long oldElements = elements;
    elements |= es.elements;
    return elements != oldElements;
  }

  /**
   * Removes from this set all of its elements that are contained in
   * the specified collection.
   *
   * @param c elements to be removed from this set
   * @return <tt>true</tt> if this set changed as a result of the call
   * @throws NullPointerException if the specified collection is null
   */
  public boolean removeAll(Collection<?> c) {
    if (!(c instanceof RegularEnumSet)) {
      return super.removeAll(c);
    }

    RegularEnumSet<?> es = (RegularEnumSet<?>) c;
    if (es.elementType != elementType) {
      return false;
    }

    long oldElements = elements;
    elements &= ~es.elements;
    return elements != oldElements;
  }

  /**
   * Retains only the elements in this set that are contained in the
   * specified collection.
   *
   * @param c elements to be retained in this set
   * @return <tt>true</tt> if this set changed as a result of the call
   * @throws NullPointerException if the specified collection is null
   */
  public boolean retainAll(Collection<?> c) {
    if (!(c instanceof RegularEnumSet)) {
      return super.retainAll(c);
    }

    RegularEnumSet<?> es = (RegularEnumSet<?>) c;
    if (es.elementType != elementType) {
      boolean changed = (elements != 0);
      elements = 0;
      return changed;
    }

    long oldElements = elements;
    elements &= es.elements;
    return elements != oldElements;
  }

  /**
   * Removes all of the elements from this set.
   */
  public void clear() {
    elements = 0;
  }

  /**
   * Compares the specified object with this set for equality.  Returns
   * <tt>true</tt> if the given object is also a set, the two sets have
   * the same size, and every member of the given set is contained in
   * this set.
   *
   * @param o object to be compared for equality with this set
   * @return <tt>true</tt> if the specified object is equal to this set
   */
  public boolean equals(Object o) {
    if (!(o instanceof RegularEnumSet)) {
      return super.equals(o);
    }

    RegularEnumSet<?> es = (RegularEnumSet<?>) o;
    if (es.elementType != elementType) {
      return elements == 0 && es.elements == 0;
    }
    return es.elements == elements;
  }
}
